grammar C;

options { language=Java; backtrack=true;}

@header {
import java.util.ArrayList;
import java.util.HashMap;
}

@members {
	private class Stack
	{
		public ArrayList<HashMap<String,String>> lst = new ArrayList<HashMap<String,String>>();
		public void push(HashMap hm)
		{
			lst.add(hm);
		}
		public HashMap pop()
		{
			return lst.remove(lst.size()-1);
		}
		public String Get(String key)
		{
			for(int i = lst.size()-1; i>=0; i--)
			{
				if(lst.get(i).containsKey(key))
				{
					return lst.get(i).get(key);
				}
			}
			return null;
		}
		public void Set(String key, String value)
		{
			lst.get(lst.size()-1).put(key,value);
		}
		public void xSet(String key, String value)
		{
			lst.get(0).put(key,value);
		}
	}
	public Stack stk = new Stack();
	public String start = "";
	public String end = "";
	public String fname = "";
}

INT
	: ('0'..'9')+
	;

BASETYPE
	: 'int'
	| 'void'
	;

ID
	: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
	;

WS
	: (' '|'\t'|'\r'|'\n')+ { $channel=HIDDEN; }
	;

program returns [String LLVM]
@after
{
	$LLVM = start+$LLVM+end;
}
	:
	{
		stk.push(new HashMap<String,String>());
	}
	 ( global[stk,1]
	 	{
			if($LLVM == null)
				$LLVM = $global.LLVM;
			else
				$LLVM += $global.LLVM;
		}
	  )* EOF 
	;

global [Stack stk, int Index] returns [String LLVM]
@init
{
	$stk.push(new HashMap<String,String>());
}
@after
{
	$stk.pop();
}
	: funcDefine[$stk,$Index] {$LLVM = $funcDefine.LLVM;}
	| (';'{$LLVM = "";})

	;

funcDefine [Stack stk,int Index] returns [String LLVM, int retIndex]
@after
{
	fname = "";
}
	: funcHeader[$stk,$Index]
	{
		$LLVM = $funcHeader.LLVM;
		$Index = $funcHeader.retIndex;
	} 
	block[$stk,$Index, true, true]
	{
		if($funcHeader.varDef != null)
			$LLVM += "\n{\n"+$funcHeader.varDef+$block.LLVM.substring(3);
		else
		{
			$LLVM += $block.LLVM;
		}
		//System.out.println($block.retIndex);
	}
	;

funcHeader[Stack stk,int Index] returns [String LLVM, int retIndex, String varDef]
@init{
	String retType = new String();
}
	:
	{
		$LLVM = "\ndefine ";
	}
	BASETYPE 
	{
		if($BASETYPE.text.equals("int"))
		{
			$LLVM += "i32";
			retType = "i32";
		}
		if($BASETYPE.text.equals("void"))
		{
			$LLVM += "void";
			retType = "void";
		}
	}
	ID 
	{
		$stk.xSet($ID.text,retType);
		$LLVM += " @";
		$LLVM += $ID.text;
		fname = $ID.text;
	}
	'('
	{
		$LLVM += "(";
	}
	( 
	(pa=singleParam [$stk, $Index]
	{
		$LLVM += $pa.LLVM;
		$Index = $pa.retIndex;
		$varDef = $pa.varDef;
	}
	)((','{$LLVM += ", ";}) 
	(pb=singleParam [$stk, $Index]
	{
		$LLVM += $pb.LLVM;
		$Index = $pb.retIndex;
		$varDef += $pb.varDef;
	}))* )? 
	')'
	{
		$LLVM += ") nounwind";
		$retIndex = $Index;
	}
	;

singleParam[Stack stk,int Index] returns [String LLVM, int retIndex, String varDef]
	: BASETYPE 
	{
		if($BASETYPE.text.equals("int"))$LLVM = "i32";
		if($BASETYPE.text.equals("void"))$LLVM = "void";
	} 
	('*'{$LLVM = $LLVM+"*";})? 
	ID 
	{
		$stk.Set($Index+"", $LLVM+"*");
		$stk.Set($ID.text, " "+$Index);
		$varDef = "\%"+$Index+" = alloca "+$LLVM+"\n";
		$varDef += "store "+$LLVM+" \%"+$ID.text+", "+$LLVM+"* \%"+$Index+"\n";
		$Index++;
		$retIndex = $Index;
		$LLVM = $LLVM+" \%"+$ID.text;
		//System.out.println($varDef);
	}
	;

block [Stack stk, int Index, boolean needBrackets, boolean needRet] returns [String LLVM, int retIndex]
	:'{'
	 {
	 	if($needBrackets)
	 	{
	 		$LLVM = "\n{\n";
	 	}
	 	
	 }
	 (varDeclearation[$stk,$Index]
	 {
	 	$Index = $varDeclearation.retIndex;
	 	if($LLVM == null)
	 		$LLVM = $varDeclearation.LLVM;
	 	else if($varDeclearation.LLVM != null)
	 		$LLVM += $varDeclearation.LLVM;
	 })* 
	 (sentence[$stk,$Index]
	 {
	 	$Index = $sentence.retIndex;
	 	$needRet = $sentence.needRet;
	 	if($LLVM == null)
	 		$LLVM = $sentence.LLVM;
	 	else if($sentence.LLVM != null)
	 		$LLVM += $sentence.LLVM;	
	 })* 
	 '}'
	 {
	 	if($needRet && $needBrackets) $LLVM+="ret void\n";
	 	if($needBrackets)$LLVM += "}\n";
	 	$retIndex = $Index;
	 	//System.out.println($retIndex);
	 }
	;

varDeclearation [Stack stk,int Index] returns [String LLVM, int retIndex]
@init
{
	//System.out.println($Index);
}
@after
{
	$retIndex = $Index;
	//System.out.println($retIndex);
}
	: BASETYPE 
	(a=singleVar[$stk,$BASETYPE.text,$Index]
	{
		$Index = $a.retIndex;
		$LLVM = $a.LLVM;
	} )
	(',' 
	b=singleVar[$stk,$BASETYPE.text,$Index]
	{
		$Index = $b.retIndex;
		$LLVM += $b.LLVM;
	})* ';'
	;

singleVar [Stack stk, String Type, int Index] returns [String LLVM,int retIndex]
@init{
	if($Type.equals("int"))
		$Type = "i32";
	//System.out.println($Index);
}
@after
{
	//System.out.println($Index+"dddddd"+$retIndex);
}
	: ('*'{$Type+="*";})? 
	ID 
	{
		$stk.Set($ID.text, $Type+"*");
		$LLVM = "\%"+$ID.text+" = alloca "+$Type+"\n";
		$retIndex = $Index;
	}
	('=' expr[$stk,$Index]
	{
		$Index = $expr.retIndex;
		$LLVM +=  $expr.LLVM;
		$stk.Set($Index+"", $stk.Get($expr.addr).substring(0, $stk.Get($expr.addr).length()-1));
		$LLVM += "\%"+$Index+" = load "+$stk.Get($expr.addr)+" \%"+$expr.addr+"\n";
		$LLVM += "store "+$stk.Get($Index+"")+" \%"+$Index+", "+$stk.Get($ID.text)+" \%"+$ID.text+"\n";
		$Index++;
		$retIndex = $Index;
		//System.out.println($LLVM);
	})?
	|  ID '[' ']' '='  exprGroup[$stk, $Index]
	{
		start += "@"+fname+"."+$ID.text+" = constant "+"["+$exprGroup.count+" x i32] "+$exprGroup.LLVM+", align 16\n";
		end = "declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i32, i1) nounwind\n";
		$stk.Set($ID.text, "["+$exprGroup.count+" x i32]*");
		$LLVM = "\%"+$ID.text+" = alloca "+"["+$exprGroup.count+" x i32], align 16\n";
		$stk.Set($Index+"", "i8*");
		$LLVM += "\%"+$Index+" = bitcast ["+$exprGroup.count+" x i32]* \%"+$ID.text+" to i8*\n";
		$LLVM += "call void @llvm.memcpy.p0i8.p0i8.i64(";
  		$LLVM += "i8* \%"+$Index+", i8* bitcast (["+$exprGroup.count+" x i32]* @"+fname+"."+$ID.text+" to i8*), i64 "+($exprGroup.count*4)+", i32 16, i1 false)\n";
  		$Index++;
  		$retIndex = $Index;
  		//System.out.println(start);
  		//System.out.println();
  		//System.out.println(end);
  		//System.out.println();
  		//System.out.println($LLVM);
  	}
	;

sentence [Stack stk, int Index] returns [String LLVM, int retIndex, boolean needRet]
@init{
	$needRet = true;
	$LLVM = "";
}
@after
{
	$retIndex = $Index;
}

	: 
	a=expr[$stk, $Index] ';'
	{
		$Index = $a.retIndex;
		$LLVM += $a.LLVM;
	}
	| 
	block[$stk, $Index, false, false]
	{
		$Index = $block.retIndex;
		$LLVM += $block.LLVM;
	}
	| 'return' b=expr[$stk, $Index] ';'
	{
		$Index = $b.retIndex;
		$LLVM += $b.LLVM;
		$stk.Set($Index+"", $stk.Get($b.addr).substring(0, $stk.Get($b.addr).length()-1));
		$LLVM += "\%"+$Index+" = load "+$stk.Get($b.addr)+" \%"+$b.addr+"\n";
		$LLVM += "ret "+$stk.Get($Index+"")+" \%"+$Index+"\n";
		$Index++;
		$LLVM += "\n;<label>:"+$Index+"\n";
		$Index++;
		$retIndex = $Index;
		$needRet = false;
		//System.out.println($LLVM);
	}
	| 
	('if' '(' 
	(c=expr[$stk, $Index] 
	{
		$Index = $c.retIndex;
	})')' 
	(d=sentence[$stk,$Index+3]
	{
		$Index = $d.retIndex;
	})
	{
		$LLVM += $c.LLVM;
		$stk.Set($c.retIndex+"", $stk.Get($c.addr).substring(0, $stk.Get($c.addr).length()-1));
		$LLVM += "\%"+$c.retIndex+" = load "+$stk.Get($c.addr)+" \%"+$c.addr+"\n";
		//$Index++;
		$stk.Set(($c.retIndex+1)+"", "i1");
		$LLVM += "\%"+($c.retIndex+1)+" = icmp ne "+$stk.Get($c.retIndex+"")+" \%"+($c.retIndex)+", 0\n";
		//$Index++;
		$LLVM+= "br i1 \%"+($c.retIndex+1)+", label \%"+($c.retIndex+2)+", label \%"+($Index)+"\n";
		//$Index+=2;
		$LLVM += "\n;<label>:"+($c.retIndex+2)+"\n";
		$LLVM += $d.LLVM; 
		$LLVM += "br label \%"+($Index)+"\n";
		$LLVM += "\n;<label>:"+($Index)+"\n";
		$Index++;
		$retIndex = $Index;
		//System.out.println($LLVM);
	})
	| 
	('for' '(' 
	(e =expr[$stk, $Index] 
	{
		$Index = $e.retIndex;
	})
	';' 
	(f=expr[$stk, $Index+1] 
	{
		$Index = $f.retIndex;
	})
	';' 
	(g=expr[$stk, $Index+3] 
	{
		$Index = $g.retIndex;
	})
	')' 
	(h = sentence[$stk, $Index+1]
	{
		$Index = $h.retIndex;
	})
	{
		$LLVM += $e.LLVM;
		$LLVM += "br label \%"+$e.retIndex+"\n";
		$LLVM += "\n;<label>:"+$e.retIndex+"\n";
		//$Index++;
		$LLVM += $f.LLVM;
		$stk.Set($f.retIndex+"", $stk.Get($f.addr).substring(0, $stk.Get($f.addr).length()-1));
		$LLVM += "\%"+$f.retIndex+" = load "+$stk.Get($f.addr)+" \%"+$f.addr+"\n";
		//$Index++;
		$stk.Set(($f.retIndex+1)+"", "i1");
		$LLVM += "\%"+($f.retIndex+1)+" = icmp ne "+$stk.Get($f.retIndex+"")+" \%"+$f.retIndex+", 0\n";
		//$Index++;
		$LLVM+= "br i1 \%"+($f.retIndex+1)+", label \%"+($g.retIndex)+", label \%"+($h.retIndex)+"\n";
		//$Index+=2;
		$LLVM += "\n;<label>:"+($f.retIndex+2)+"\n";
		$LLVM += $g.LLVM; 
		$LLVM += "br label \%"+$e.retIndex+"\n";
		$LLVM += "\n;<label>:"+($g.retIndex)+"\n";
		$LLVM += $h.LLVM; 
		$LLVM += "br label \%"+($f.retIndex+2)+"\n";
		$LLVM += "\n;<label>:"+$Index+"\n";
		$Index++;
		$retIndex = $Index;
		//System.out.println($LLVM);
	})
	;

expr [Stack stk, int Index] returns [String LLVM, String addr, int retIndex]
	: 
	(exprlv6[$stk, $Index] 
	{
		$Index = $exprlv6.retIndex;
		$LLVM = $exprlv6.LLVM;
		
	})
	(opexprlv14[$stk, $Index]  
	{
		$Index = $opexprlv14.retIndex;
		$LLVM += $opexprlv14.LLVM;
		if($opexprlv14.addr.equals(""))
		{
			$LLVM = $exprlv6.LLVM;
			$retIndex = $exprlv6.retIndex;
			$addr = $exprlv6.addr;
		}
		else
		{
			$stk.Set($Index+"", $stk.Get($opexprlv14.addr).substring(0,$stk.Get($opexprlv14.addr).length()-1));
			$LLVM += "\%"+$Index+" = load "+$stk.Get($opexprlv14.addr)+" \%"+$opexprlv14.addr+"\n";
			$LLVM += "store "+$stk.Get($Index+"")+" \%"+$Index+", "+$stk.Get($exprlv6.addr)+" \%"+$exprlv6.addr+"\n";
			$Index++;
			$retIndex = $Index;
			$addr = $exprlv6.addr;
			//System.out.println($LLVM+"\n");
		}
		//System.out.println($LLVM+"\n");
		
	})
	;
opexprlv14[Stack $stk, int Index] returns [String LLVM, String addr, int retIndex]
	: 
	('=' exprlv6[$stk, $Index]
	{
		$LLVM = $exprlv6.LLVM;
		$addr = $exprlv6.addr;
		$retIndex = $exprlv6.retIndex;
	})
	| 
	{
		$LLVM = "";
		$addr = "";
		$retIndex = $Index;
	}
	;
	
exprlv6[Stack stk, int Index] returns [String LLVM, String addr, int retIndex]
	: 
	(exprlv4[$stk, $Index] 
	{
		$Index = $exprlv4.retIndex;
		$LLVM = $exprlv4.LLVM;
	}) 
	(opexprlv6[$stk, $Index]
	{
		$Index = $opexprlv6.retIndex;
		$LLVM += $opexprlv6.LLVM;
		if($opexprlv6.tp == 0)
		{
			$retIndex = $Index;
			$addr = $exprlv4.addr;
		}
		else
		{
			$stk.Set($Index+"", $stk.Get($exprlv4.addr).substring(0, $stk.Get($exprlv4.addr).length()-1));
			$LLVM += "\%"+$Index+" = load "+$stk.Get($exprlv4.addr)+" \%"+$exprlv4.addr+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get($opexprlv6.addr).substring(0, $stk.Get($opexprlv6.addr).length()-1));
			$LLVM += "\%"+$Index+" = load "+$stk.Get($opexprlv6.addr)+" \%"+$opexprlv6.addr+"\n";
			$Index++;
  			$stk.Set($Index+"","i1");
  			if($opexprlv6.tp == -1)
  				$LLVM += "\%"+$Index+" = icmp slt "+$stk.Get(($Index-1)+"")+" \%"+($Index-2)+", \%"+($Index-1)+"\n";
  			else
 				$LLVM += "\%"+$Index+" = icmp sgt "+$stk.Get(($Index-1)+"")+" \%"+($Index-2)+", \%"+($Index-1)+"\n";
 			$Index++;
 			$stk.Set($Index+"", "i1*");
 			$LLVM += "\%"+$Index+" = alloca i1\n";
 			$LLVM += "store i1 \%"+($Index-1)+", i1* \%"+$Index+"\n";
  			$addr = $Index+"";
 			$Index++;
 			$retIndex = $Index;
 			//System.out.println($LLVM);
		}
	})
	;

opexprlv6[Stack stk, int Index] returns [String LLVM, String addr, int retIndex, int tp]
	: 
	('<' a=exprlv4[$stk, $Index]
	{
		$tp = -1;
		$LLVM = $a.LLVM;
		$addr = $a.addr;
		$retIndex = $a.retIndex;
	})
	| 
	('>' b=exprlv4[$stk, $Index]
	{
		$tp = 1;
		$LLVM = $b.LLVM;
		$addr = $b.addr;
		$retIndex = $b.retIndex;
	})
	| 
	{
		$LLVM = "";
		$addr = "";
		$retIndex = $Index;
		$tp = 0;
	}
	;

exprlv4 [Stack stk, int Index] returns [String LLVM, String addr, int retIndex]
	:
	(exprlv2[$stk, $Index] 
	{
		$Index = $exprlv2.retIndex;
	})
	(opexprlv4[$stk, $Index]
	{
		$LLVM = $exprlv2.LLVM+$opexprlv4.LLVM;
		$Index = $opexprlv4.retIndex;
		if($opexprlv4.addr.equals(""))
		{
			$LLVM = $exprlv2.LLVM;
			$addr = $exprlv2.addr;
			$retIndex = $exprlv2.retIndex;
		}
		else
		{
			//$Index = 10;
			$stk.Set($Index+"", $stk.Get($exprlv2.addr).substring(0, $stk.Get($exprlv2.addr).length()-1));
			$LLVM += "\%"+$Index+" = load "+$stk.Get($exprlv2.addr)+" \%"+$exprlv2.addr+"\n";;
			$Index++;
			$stk.Set($Index+"", $stk.Get($opexprlv4.addr).substring(0, $stk.Get($opexprlv4.addr).length()-1));
			$LLVM += "\%"+$Index+" = load "+$stk.Get($opexprlv4.addr)+" \%"+$opexprlv4.addr+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get(($Index-1)+""));
			$LLVM += "\%"+$Index+" = add nsw "+$stk.Get(($Index-1)+"")+" \%"+($Index-1)+", \%"+($Index-2)+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get(($Index-1)+"")+"*");
			$LLVM += "\%"+$Index+" = alloca "+$stk.Get(($Index-1)+"")+"\n";
			$LLVM += "store "+ $stk.Get(($Index-1)+"")+" \%"+($Index-1)+", "+$stk.Get($Index+"")+" \%"+$Index+"\n";
			$Index++;
			$retIndex = $Index;
			$addr = ($Index-1)+"";
			//System.out.println($LLVM);
		}
	})
	;

opexprlv4 [Stack stk, int Index] returns [String LLVM, String addr, int retIndex]
	: 
	('+' a=exprlv2[$stk, $Index] {$Index = $a.retIndex;}b=opexprlv4[$stk, $Index]
	{
		$Index = $b.retIndex;
		$LLVM = $a.LLVM+$b.LLVM;
		if($b.addr.equals(""))
		{
			$LLVM = $a.LLVM;
			$addr = $a.addr;
			$retIndex = $a.retIndex;
		}
		else
		{
			//$Index = 10;
			$stk.Set($Index+"", $stk.Get($b.addr).substring(0, $stk.Get($b.addr).length()-1));
			$LLVM += "\%"+$Index+" = load "+$stk.Get($b.addr)+" \%"+$b.addr+"\n";;
			$Index++;
			$stk.Set($Index+"", $stk.Get($a.addr).substring(0, $stk.Get($a.addr).length()-1));
			$LLVM += "\%"+$Index+" = load "+$stk.Get($a.addr)+" \%"+$a.addr+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get(($Index-1)+""));
			$LLVM += "\%"+$Index+" = add nsw "+$stk.Get(($Index-1)+"")+" \%"+($Index-1)+", \%"+($Index-2)+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get(($Index-1)+"")+"*");
			$LLVM += "\%"+$Index+" = alloca "+$stk.Get(($Index-1)+"")+"\n";
			$LLVM += "store "+ $stk.Get(($Index-1)+"")+" \%"+($Index-1)+", "+$stk.Get($Index+"")+" \%"+$Index+"\n";
			$Index++;
			$retIndex = $Index;
			$addr = ($Index-1)+"";
			//System.out.println($LLVM);
		}
	})
	| 
	('-' c=exprlv2[$stk, $Index] {$Index = $c.retIndex;} d=opexprlv4[$stk, $Index]
	{
		$LLVM = $c.LLVM+$d.LLVM;
		$Index = $d.retIndex;
		if($d.addr.equals(""))
		{
			//$Index = 10;
			$stk.Set($Index+"", $stk.Get($c.addr).substring(0, $stk.Get($c.addr).length()-1));
			$LLVM += "\%"+$Index+" = load "+$stk.Get($c.addr)+" \%"+$c.addr+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get(($Index-1)+""));
			$LLVM += "\%"+$Index+" = sub nsw "+$stk.Get(($Index-1)+"")+" 0, \%"+($Index-1)+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get(($Index-1)+"")+"*");
			$LLVM += "\%"+$Index+" = alloca "+$stk.Get(($Index-1)+"")+"\n";
			$LLVM += "store "+ $stk.Get(($Index-1)+"")+" \%"+($Index-1)+", "+$stk.Get($Index+"")+" \%"+$Index+"\n";
			$addr = $Index+"";
			$Index++;
			$retIndex = $Index;
			//System.out.println($LLVM);
		}
		else
		{
			//$Index = 10;
			//System.out.println($d.text + "  " +  $stk.Get($d.addr));
			$stk.Set($Index+"", $stk.Get($d.addr).substring(0, $stk.Get($d.addr).length()-1));
			$LLVM += "\%"+$Index+" = load "+$stk.Get($d.addr)+" \%"+$d.addr+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get($c.addr).substring(0, $stk.Get($c.addr).length()-1));
			$LLVM += "\%"+$Index+" = load "+$stk.Get($c.addr)+" \%"+$c.addr+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get(($Index-1)+""));
			$LLVM += "\%"+$Index+" = sub nsw "+$stk.Get(($Index-1)+"")+" \%"+($Index-1)+", \%"+($Index-2)+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get(($Index-1)+"")+"*");
			$LLVM += "\%"+$Index+" = alloca "+$stk.Get($Index+"")+"\n";
			$LLVM += "store "+ $stk.Get(($Index-1)+"")+" \%"+($Index-1)+", "+$stk.Get($Index+"")+" \%"+$Index+"\n";
			$Index++;
			$retIndex = $Index;
			$addr = ($Index-1)+"";
			//System.out.println($LLVM);	
		}
	})
	| 
	{
		$LLVM = "";
		$addr = "";
		$retIndex = $Index;
	}
	;

exprlv2 [Stack stk, int Index] returns [String LLVM, String addr, int retIndex]
	: 
	(ID '++'
	{
		//$Index = 5;
		String temp = $stk.Get($ID.text);
	 	String otname;
	 	if(temp != null && temp.substring(0,1).equals(""))
	 	{
	 		otname = temp.substring(1);
	 	}
	 	else
	 	{
	 		otname = $ID.text;
	 	}
		$stk.Set($Index+"", $stk.Get(otname).substring(0, $stk.Get(otname).length()-1));
		$LLVM = "\%"+$Index+" = load "+$stk.Get(otname)+" \%"+otname+"\n";
		$Index++;
		$stk.Set($Index+"", $stk.Get(($Index-1)+""));
		$LLVM += "\%"+$Index+" = add nsw "+$stk.Get($Index+"")+" \%"+ ($Index-1) + ", 1\n";
		$LLVM += "store "+$stk.Get($Index+"")+" \%"+$Index+", "+$stk.Get(otname)+" \%"+otname+"\n";
		$Index++;
		$addr = otname;
		$retIndex = $Index;
		//System.out.println($LLVM);
	})
	|
	('&' a=exprlv2[$stk, $Index]
	{
		$Index = $a.retIndex;
		$LLVM = $a.LLVM;
		$stk.Set($Index+"", $stk.Get($a.addr)+"*");
	 	$LLVM += "\%"+$Index+" = alloca "+$stk.Get($a.addr)+"\n";
	 	$LLVM += "store "+$stk.Get($a.addr)+" \%"+$a.addr+", "+$stk.Get($a.addr)+"* "+"\%"+$Index+"\n";
	 	$addr = $Index+"";
	 	$Index++;
	 	$retIndex = $Index;
	 	//System.out.println($LLVM);
	})
	|
	('*' b=exprlv2[$stk, $Index]
	{
		$Index = $b.retIndex;
		$LLVM = $b.LLVM;
		$stk.Set($Index+"", $stk.Get($b.addr).substring(0,$stk.Get($b.addr).length()-1));
		$LLVM += "\%"+$Index+" = load "+$stk.Get($b.addr)+" \%"+$b.addr+"\n";
		$addr = $Index+"";
		$Index++;
		$retIndex = $Index;
		//System.out.println($LLVM);
	})
	|
	('-' c=exprlv2[$stk, $Index]
	{
		$Index = $c.retIndex;
		$LLVM = $c.LLVM;
		$stk.Set($Index+"", $stk.Get($c.addr).substring(0,$stk.Get($c.addr).length()-1));
		$LLVM += "\%"+$Index+" = load "+$stk.Get($c.addr)+" \%"+$c.addr+"\n";
		$Index++;
		$stk.Set($Index+"", $stk.Get(($Index-1)+""));
		$LLVM += "\%"+$Index+" = mul "+$stk.Get(($Index-1)+"")+" \%"+($Index-1)+", -1\n";
		$LLVM += "store "+$stk.Get($Index+"")+" \%"+$Index+", "+$stk.Get($c.addr)+" \%"+$c.addr+"\n";
		$Index++;
		$retIndex = $Index;
		$addr=$c.addr;
		//System.out.println($LLVM);
	})
	| 
	('!' d=exprlv2[$stk, $Index]
	{
		//System.out.println();
		$Index = $d.retIndex;
		$LLVM = $d.LLVM;
		$stk.Set($Index+"", $stk.Get($d.addr).substring(0,$stk.Get($d.addr).length()-1));
		$LLVM += "\%"+$Index+" = load "+$stk.Get($d.addr)+" \%"+$d.addr+"\n";
		$Index++;
		$stk.Set($Index+"", $stk.Get(($Index-1)+""));
		$LLVM += "\%"+$Index+" = xor "+$stk.Get(($Index-1)+"")+" \%"+($Index-1)+", -1\n";
		$LLVM += "store "+$stk.Get($Index+"")+" \%"+$Index+", "+$stk.Get($d.addr)+" \%"+$d.addr+"\n";
		$Index++;
		$retIndex = $Index;
		$addr=$d.addr;
		//System.out.println($LLVM);
	})
	| 
	(exprlv1[$stk, $Index]
	{
		$LLVM = $exprlv1.LLVM;
		$addr = $exprlv1.addr;
		$retIndex = $exprlv1.retIndex;
	})
	;


exprlv1 [Stack stk, int Index] returns [String LLVM, String addr, int retIndex]
@init{
	$LLVM = "";
}
	:(atom[$stk, $Index]{$Index = $atom.retIndex;}) array[$stk, $Index]
	{
		//System.out.println($array.retIndex);
		$Index = $array.retIndex;
		if($array.addr == "")
		{
			$LLVM = $atom.LLVM;
			$retIndex = $atom.retIndex;
			$addr = $atom.addr;
		}
		else
		{
			//$Index = $array.retIndex;
			$LLVM = $atom.LLVM + $array.LLVM;
			String temp = new String();
			//System.out.println($atom.text+$array.text);
			//System.out.println($atom.addr);
			//System.out.println($stk.Get($atom.addr));
			$stk.Set($Index+"", $stk.Get($atom.addr).substring(0, $stk.Get($atom.addr).length()-1));
			temp = "\%"+$Index+" = load "+$stk.Get($atom.addr)+" \%"+$atom.addr+"\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get($array.addr).substring(0, $stk.Get($array.addr).length()-1));
			temp += "\%"+$Index+" = load "+$stk.Get($array.addr)+" \%"+$array.addr+"\n";
			$Index++;
			$stk.Set($Index+"", "i64");
			temp += "\%"+$Index+" = sext i32 \%"+($Index-1)+" to i64\n";
			$Index++;
			$stk.Set($Index+"", $stk.Get(($Index-3)+""));
			temp += "\%"+$Index+" = getelementptr inbounds "+$stk.Get(($Index-3)+"")+" \%"+($Index-3)+", "+$stk.Get(($Index-1)+"")+" \%"+($Index-1)+"\n";
			$addr = $Index + "";
			$Index++;
			if($LLVM == null)
				$LLVM = temp;
			else
				$LLVM += temp;
			$retIndex = $Index;
			//System.out.println(temp);
		}
	}
	;

array [Stack stk, int Index] returns [String LLVM, String addr, int retIndex]
	:'[' expr[$stk, $Index] ']'
	{
		$LLVM = $expr.LLVM;
		$addr = $expr.addr;
		$retIndex = $expr.retIndex;
	}
	|
	{
		$LLVM = "";
		$addr = "";
		$retIndex = $Index;
	}
	;

atom [Stack stk, int Index] returns [String LLVM, String addr, int retIndex]
	: INT
	 {
	 	$stk.Set($Index+"","i32*");
	 	$LLVM = "\%"+$Index+" = alloca i32\n";
	 	$LLVM += "store i32 " + $INT.text+", i32* \%"+$Index+"\n";
	 	$addr = $Index+"";
	 	$Index++;
	 	$retIndex = $Index;
	 }
	| functionCall[$stk, $Index]
	 {
	 	$LLVM = $functionCall.LLVM;
	 	$addr = $functionCall.addr;
	 	$retIndex = $functionCall.retIndex;
	 }
	| ID
	 {
	 	String temp = $stk.Get($ID.text);
	 	String otname;
	 	if(temp != null && temp.substring(0,1).equals(" "))
	 	{
	 		$LLVM = "";
	 		otname = temp.substring(1);
	 	}
	 	else if(temp != null && temp.substring(0,1).equals("["))
	 	{
	 		String t = temp.substring(temp.indexOf('x')+1, temp.indexOf(']'));
	 		$stk.Set($Index+"", t+"*");
	 		$LLVM = "\%"+$Index+" = getelementptr inbounds "+temp+" \%"+$ID.text+", "+t+" 0, "+t+" 0\n";
	 		//System.out.println($LLVM);
	 		$Index++;
	 		$stk.Set($Index+"", $stk.Get(($Index-1)+"")+"*");
	 		$LLVM += "\%"+$Index+" = alloca "+$stk.Get(($Index-1)+"")+"\n";
	 		$LLVM += "store "+$stk.Get(($Index-1)+"")+" \%"+($Index-1)+", "+$stk.Get($Index+"")+" \%"+$Index+"\n";
	 		otname = $Index+"";
	 		$Index++;
	 		//System.out.println($LLVM);
	 	}
	 	else
	 	{
	 		$LLVM = "";
	 		otname = $ID.text;
	 	}
	 	$retIndex = $Index;
	 	$addr = otname;
	 	//System.out.println($ID.text+"     "+$addr);
	 	//$stk.Set($Index+"", stk.Get($ID.text)+"*");
	 	//$LLVM = "\%"+$Index+" = alloca "+stk.Get($ID.text)+"\n";
	 	//$LLVM += "store "+stk.Get($ID.text)+" \%"+$ID.text+", "+stk.Get($ID.text)+"* "+"\%"+$Index+"\n";
	 	//$addr = $Index+"";
	 	//$Index++;
	 	//$retIndex = $Index;
	 	//System.out.println(otname);
	 }
	| '(' expr[$stk, $Index]')'
	 {
	 	$LLVM = $expr.LLVM;
	 	$addr = $expr.addr;
	 	$retIndex = $expr.retIndex;
	 }
	;

functionCall[Stack stk, int Index] returns [String LLVM, String addr, int retIndex]
@init{
	String add = new String();
	String type = new String();
}
@after
{
	if(!type.equals("void"))
	{
		$stk.Set($Index+"",type);
		add = "\%"+$Index + " = "+add+")\n";
		$LLVM += add;
		$Index++;
		$stk.Set($Index+"", $stk.Get(($Index-1)+"")+"*");
	 	$LLVM += "\%"+$Index+" = alloca "+$stk.Get(($Index-1)+"")+"\n";
	 	$LLVM += "store "+stk.Get(($Index-1)+"")+" \%"+($Index-1)+", "+$stk.Get(($Index-1)+"")+"* "+"\%"+$Index+"\n";
		$addr = $Index+"";
		$Index++;
		$retIndex = $Index;
	}
	else
	{
		$LLVM += add+")\n";
		$addr = "";
		$retIndex = $Index;
	}
}
	: ID
	{
		type = $stk.Get($ID.text);
		add = "call " + $stk.Get($ID.text) + "@" + $ID.text+"(";
	}
	'(' ( 
	a=expr[$stk, $Index]
	{
		//System.out.println($a.text);
		$Index = $a.retIndex;
		if($a.LLVM != null)
			$LLVM = $a.LLVM;
		$stk.Set($Index+"", $stk.Get($a.addr).substring(0,$stk.Get($a.addr).length()-1));
		if($LLVM == null)
			$LLVM = "\%"+$Index+" = load "+$stk.Get($a.addr)+" \%"+$a.addr+"\n";
		else
			$LLVM += "\%"+$Index+" = load "+$stk.Get($a.addr)+" \%"+$a.addr+"\n";
		add += $stk.Get($Index+"")+" \%"+$Index;
		$Index++;
	}
	(',' 
	b=expr[$stk, $Index]
	{
		$Index = $b.retIndex;
		if($b.LLVM != null)
			$LLVM += $b.LLVM;
		$stk.Set($Index+"", $stk.Get($b.addr).substring(0,$stk.Get($b.addr).length()-1));
		$LLVM += "\%"+$Index+" = load "+$stk.Get($b.addr)+" \%"+$b.addr+"\n";
		add += ", "+$stk.Get($Index+"")+" \%"+$Index;
		$Index++;
	}
	)* )? ')'
	{
		/*if(add.substring(0,1).equals("\%"))
		{
			$stk.Set($Index+"", $stk.Get($addr)+"*");
	 		$LLVM += "\%"+$Index+" = alloca "+stk.Get($addr)+"\n";
	 		$LLVM += "store "+stk.Get($addr)+" \%"+$addr+", "+stk.Get($addr)+"* "+"\%"+$Index+"\n";
	 		$addr = $Index+"";
	 		$Index++;
	 		$retIndex = $Index;
		}
		else
		{
			$addr = "null";
			$retIndex = $Index;
		}*/
		//System.out.println($LLVM);
	}
	;
exprGroup[Stack stk, int Index] returns [String LLVM, int count]
@init
{
	$LLVM = "[";
	$count = 0;
}
@after
{
	$LLVM += "]";
}
	: '{' 
	a = INT
	{
		$LLVM += "i32 "+ $a.text;
		$count++;
	} 
	(',' 
	b = INT
	{
		$LLVM += ", i32 "+ $b.text;
		$count++;
	})* '}'
	;
